package edu.mit.csail.relo.jdt.builder.asm;

import java.util.ArrayList;
import java.util.List;

import edu.mit.csail.relo.jdt.CodeUnit;
import edu.mit.csail.relo.jdt.RJCore;
import edu.mit.csail.relo.jdt.builder.ReloRDFBuildProcessor;
import edu.mit.csail.relo.jdt.extractors.MethodParametersExtractor;
import edu.mit.csail.relo.modelBridge.Artifact;
import edu.mit.csail.relo.modelBridge.DirectedRel;

// this build processor adds about 12s to building the lapis project!
//@tag design-todo: unify transformer design pattern
public class InferOverridesBuildProcessor extends ReloRDFBuildProcessor {

	interface Transformer {
		void transform(Artifact codeArt);
	}

	class Joiner implements Transformer {
		public void transform(Artifact currClass) {
    		List<Artifact> currMethsLookingForOverrides = currClass.getChildrenArtifacts(rdfRepo, CodeUnit.getTypeFilter(rdfRepo, RJCore.methodType));
    		getSuperClassesToCheckForOverrides(currClass, currMethsLookingForOverrides);
		}

		private void getSuperClassesToCheckForOverrides(Artifact currClass, List<Artifact> currMethsLookingForOverrides) {
			List<Artifact> superClasses = currClass.listArt(rdfRepo, DirectedRel.getFwd(RJCore.inherits));
			for (Artifact superClass : superClasses) {
				checkForOverrides(superClass, currMethsLookingForOverrides);
	    		getSuperClassesToCheckForOverrides(superClass, currMethsLookingForOverrides);
			}
		}

		private void checkForOverrides(Artifact superClass, List<Artifact> currMethsLookingForOverrides) {
			List<Artifact> superMeths = superClass.getChildrenArtifacts(rdfRepo, CodeUnit.getTypeFilter(rdfRepo, RJCore.methodType));
    		for (Artifact currMeth : new ArrayList<Artifact>(currMethsLookingForOverrides)) {
    			String currMethName = currMeth.getName(rdfRepo);
    			String currMethSig = MethodParametersExtractor.getInOutSig(currMeth, rdfRepo, false);
    			for (Artifact superMeth : superMeths) {
        			String superMethName = superMeth.getName(rdfRepo);
        			String superMethSig = MethodParametersExtractor.getInOutSig(superMeth, rdfRepo, false);
        			if (!currMethName.equals(superMethName)) continue;
        			if (!currMethSig.equals(superMethSig)) continue;
        			
        			currMethsLookingForOverrides.remove(currMeth);
        			
        			rdfRepo.addStatement(currMeth.elementRes, RJCore.overrides, superMeth.elementRes);
				}
			}
		}
	}

	class Unjoiner implements Transformer {
		public void transform(Artifact currClass) {
    		List<Artifact> currMeths = currClass.getChildrenArtifacts(rdfRepo, CodeUnit.getTypeFilter(rdfRepo, RJCore.methodType));
    		for (Artifact currMeth : currMeths) {
    			rdfRepo.removeStatements(currMeth.elementRes, RJCore.overrides, null);
    		}
		}
		
	}

	@Override
	public void finishProcessing() {
	    transform(projArt, new Joiner());
    }
    
	@Override
	public void cleanAnnotations() {
	    transform(projArt, new Unjoiner());
    }

    private void transform(Artifact codeArt, Transformer transformer) {
		// do a depth first traversal of the containment heirarchy while 'transforming'
		// 'transforming' = {preCondition: search for anonymous methods} do a join on method calls

    	List<Artifact> children = codeArt.getChildrenArtifacts(rdfRepo);
    	for (Artifact child : children) {
			transform(child, transformer);
		}
    	if (!rdfRepo.hasStatement(codeArt.elementRes, rdfRepo.rdfType, RJCore.classType)) return;
    	
    	transformer.transform(codeArt);
	}

}
